// 此hpp文件用于形成基于环形队列的生产消费模型，且使用信号量完成同步和互斥
#pragma once
#include <vector>
#include <semaphore.h>
#include <pthread.h>
#include "lockProtect.hpp"

const int defaultsize = 5;

template <class T>
class RingBuffer
{
public:
    RingBuffer(int size = defaultsize)
        : _rb(size), _size(size), _productor_pos(0), _consumer_pos(0)
    {
        //自动初始化好sem和mutex
        sem_init(&_sem_space, 0, _size);
        sem_init(&_sem_resourse, 0, 0);
        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }

    ~RingBuffer()
    {
        //结束时，自动析构sem和mutex
        sem_destroy(&_sem_space);
        sem_destroy(&_sem_resourse);
        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }

    void Push(T &data)
    {
        // 什么时候允许生产？ -》有空间的时候
        P(_sem_space); 
        {
            //这里加锁在信号量后面，是可以让线程先拿到sem，又让资源被唯一访问
            ProtectResourse PR(&_productor_mutex);
            _rb[_productor_pos++] = data;
            _productor_pos %= _size;
        }
        // 生产完，有资源了，让资源信号量++
        V(_sem_resourse);
    }

    void Pop(T *data)
    {
        // 什么时候允许消费？ -》有资源的时候
        P(_sem_resourse);
        {
            ProtectResourse PR(&_consumer_mutex);
            *data = _rb[_consumer_pos++];
            _consumer_pos %= _size;
        }
        // 消费完，有空间了，让空间信号量++
        V(_sem_space);
    }

private:
    std::vector<T> _rb;               // 环形队列：使用数组来表示，让其在0～(size - 1)中不断循环
    int _size;                        // 环形队列的大小
    sem_t _sem_space;                 // 给生产者的信号量，默认最开始是有size个资源的
    sem_t _sem_resourse;              // 给消费者的信号量，默认组开始是没有资源的，为0
    int _productor_pos;               // 生产者下标
    int _consumer_pos;                // 消费者下标
    pthread_mutex_t _productor_mutex; // 生产者锁，保证多生产者之间的互斥
    pthread_mutex_t _consumer_mutex;  // 消费者锁，保证消费者之间的互斥

    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }

    void V(sem_t &sem)
    {
        sem_post(&sem);
    }

};

template <class T>
class Productor_C
{
public:
    Productor_C(RingBuffer<T>* RB)
        : _RB(RB)
    {}

    std::string GetName()
    {
        static int n = 1;
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "productor-%d", n++);
        return buffer;
    }

public:
    RingBuffer<T>* _RB;
};

template <class T>
class Consumer_C
{
public:
    Consumer_C(RingBuffer<T>* RB)
        : _RB(RB)
    {}

    std::string GetName()
    {
        static int n = 1;
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "consumer-%d", n++);
        return buffer;
    }

public:
    RingBuffer<T>* _RB;
};